package com.geekydoo;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.logging.Logger;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

/**
 * The purpose of this class is to provide other classes the ability to convert
 * valid user input strings into calendar objects that can be used by the
 * program.
 * 
 * @author Patrick Dutch
 */
public class XmlCalendarUtil {

	private static Logger logger = Logger.getLogger("geekydoo");

	// Separators
	private static final String SEPARATOR_DATE = "-";
	private static final String SEPARATOR_TIME = ":";

	// Length of arrays containing short dates
	private static final int LENGTH_SHORT_DATE = 2;

	// Error messages that could be generated by this class
	private static final String ERROR_UNEXPECTED = "Something went wrong and execution cannot continue...";

	// Descriptive name for the empty string
	private static final String EMPTY_STRING = "";

	/**
	 * This method returns the XMLGregorianCalendar corresponding to the
	 * timeString argument, which is a valid time of the form HH:MM.
	 * 
	 * @param timeString A valid time of the from HH:MM
	 * @return The XMLGregorianCalendar corresponding to the timeString argument
	 * @throws GeekyDooException If an error occurs while trying to get an
	 *             XMLGregorianCalendar
	 */
	public static XMLGregorianCalendar getCalendarFromTimeString(String timeString) throws GeekyDooException {
		assert (InputValidation.isValidTimeString(timeString));
		String[] timeTokens = timeString.split(SEPARATOR_TIME);
		XMLGregorianCalendar xmlCal;
		try {
			xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
		}
		catch (DatatypeConfigurationException e) {
			GeekyDooException gde = new GeekyDooException(ERROR_UNEXPECTED, e);
			throw gde;
		}

		xmlCal.setHour(Integer.parseInt(timeTokens[0]));
		xmlCal.setMinute(Integer.parseInt(timeTokens[1]));
		xmlCal.setSecond(0);

		logger.info("Returing a calendar representing " + timeString);
		return xmlCal;
	}

	/**
	 * This method returns the XMLGregorianCalendar corresponding to the
	 * dateString argument, which is a valid date of the form DD-MM-YYYY or
	 * DD-MM. If dateString is of the form DD-MM then the returned
	 * XMLGregorianCalendar will have a year equal to the current year as
	 * determined by the system.
	 * 
	 * @param dateString A valid date of the form DD-MM-YYYY or DD-MM
	 * @return The XMLGregorianCalendar corresponding to the dateString argument
	 * @throws GeekyDooException If an error occurs while trying to get an
	 *             XMLGregorianCalendar
	 */
	public static XMLGregorianCalendar getCalendarFromDateString(String dateString) throws GeekyDooException {
		assert (InputValidation.isValidDateString(dateString));
		String[] dateTokens = dateString.split(SEPARATOR_DATE);
		XMLGregorianCalendar xmlCal;
		try {
			xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
		}
		catch (DatatypeConfigurationException e) {
			GeekyDooException gde = new GeekyDooException(ERROR_UNEXPECTED, e);
			throw gde;
		}

		xmlCal.setDay(Integer.parseInt(dateTokens[0]));
		xmlCal.setMonth(Integer.parseInt(dateTokens[1]));

		if (dateTokens.length == 3) {
			xmlCal.setYear(Integer.parseInt(dateTokens[2]));
		}
		else {
			xmlCal.setYear(GregorianCalendar.getInstance().get(Calendar.YEAR));
		}

		logger.info("Returning a calendar representing " + dateString);
		return xmlCal;
	}

	/**
	 * This method standardizes the format of a valid date and returns the date
	 * in this standardized form, which is DD-MM-YYYY. Note that the returned
	 * form uses single digit days and months if possible. (Example: 1-1-2011
	 * instead of 01-01-2011)
	 * 
	 * @param dateString The date string to be standardized
	 * @return The standardized version of the dateString argument
	 */
	public static String standardizeDateString(String dateString) {
		assert (InputValidation.isValidDateString(dateString));
		dateString = dateString.replaceAll("[/ .]", SEPARATOR_DATE);

		String day = EMPTY_STRING + Integer.parseInt(dateString.split(SEPARATOR_DATE)[0]);
		String month = EMPTY_STRING + Integer.parseInt(dateString.split(SEPARATOR_DATE)[1]);
		String year = EMPTY_STRING;

		if (dateString.split(SEPARATOR_DATE).length == LENGTH_SHORT_DATE) {
			year = EMPTY_STRING + GregorianCalendar.getInstance().get(Calendar.YEAR);
		}
		else {
			year = dateString.split(SEPARATOR_DATE)[2];
		}

		dateString = day + SEPARATOR_DATE + month + SEPARATOR_DATE + year;
		assert (InputValidation.isValidDateString(dateString));
		return dateString;
	}

	/**
	 * This method standardizes the format of a valid time and returns the time
	 * in this standardized form, which is HH:MM. Note that the returned form
	 * uses single digit hours and minutes if possible. (Example: 9:9 instead of
	 * 09:09)
	 * 
	 * @param timeString The time string to be standardized
	 * @return The standardized version of the timeString argument
	 */
	public static String standardizeTimeString(String timeString) {
		assert (InputValidation.isValidTimeString(timeString));
		String hour = EMPTY_STRING + Integer.parseInt(timeString.split(SEPARATOR_TIME)[0]);
		String minute = EMPTY_STRING + Integer.parseInt(timeString.split(SEPARATOR_TIME)[1]);

		timeString = hour + SEPARATOR_TIME + minute;
		assert (InputValidation.isValidTimeString(timeString));
		return timeString;
	}

}
